% -*- coding: utf-8 -*-
% Ada 95 品质和风格
% 版权所有 (C) 2009,2010, 朱群英
% Copyright (C) 2009,2010, Zhu Qun-Ying
%
% 本书的 TeX 代码和由之生成的 ps，pdf，html，等其他格式的文件
% 遵循 GNU通用公共授权第三版或其后的版本。
%
% 本书是基于有益的目的而加以发布，然而不负任何担保责任。
%
% 您应已收到附随于本书的GNU通用公共授权的副本；如果没有，
% 请参考 <http://www.gnu.org/licenses/gpl.html>

\chapter{可读性}
\label{c:readability}

本章推荐如何使用Ada的功能，使得代码更易阅读和理解。有许多有关注释和可读性的
迷思。命名和代码结构比注释对真正的可读性承担更多的责任。拥有和代码一样多
的注释并不代表可读性；它更可能是作者不理解什么是需要沟通的重要部分。

\section{拼写}
源代码中的拼写规则包括大写、下划线的应用、数字和缩写。如果你一致遵循这些规则，
那写出来的代码就会更清晰和易读。

\subsection{下划线的应用}
\glentry{准则}
\begin{itemize}
    \item 用下划线分隔复合名字中的词。
\end{itemize}

\glentry{范例}
\begin{blockindent}
\noindent
\begin{lstlisting}
Miles_Per_Hour
Entry_Value
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
当一个标识符包括多个词，如果用下划线分隔词，那会更容易阅读。事实上，
英语中也有这样的惯例，复合词用练字符或空格分隔。另外，为了促进代码的
可读性，如果在名字中用下划线，代码格式器对于大小写有更多的控制。
参见准则\ref{s:readability:cap}。
\end{blockindent}

\subsection{数字}
\glentry{准则}
\begin{itemize}
    \item 使用一致的风格来呈现数字。
    \item 使用对问题适当的根植来呈现字面值。
    \item 使用下划线来分隔数字，就像在普通文字中用逗号或句号
(或对于非十进制用空格)。
    \item 当使用科学标示法，用一致的大写或小写的E。
    \item 在非十进制数字中，要么使用全大写或小写来标示其中的字母。
\end{itemize}

\glentry{具体事例}
\begin{blockindent}
\noindent
\begin{itemize}
    \item[-] 十进制或八进制数字，在小数点的左边，每三个数字一组，在右边，
每五个数字一组。
    \item[-] 科学标示中，总用大写字母E。
    \item[-] 用大写字母表示在十以上进制的数字。
    \item[-] 十六进制数字，小数点两边都是每四个数字一组。
\end{itemize}
\end{blockindent}

\glentry{范例}
\begin{blockindent}
\noindent
\begin{lstlisting}
type Maximum_Samples     is range          1 .. 1_000_000;
type Legal_Hex_Address   is range   16#0000# ..   16#FFFF#;
type Legal_Octal_Address is range 8#000_000# .. 8#777_777#;

Avogadro_Number : constant := 6.02216_9E+23;
\end{lstlisting}
用一下方式表示常量$1/3$:
\begin{lstlisting}
One_Third : constant := 1.0 / 3.0;
\end{lstlisting}
或
\begin{lstlisting}
One_Third_Base_3 : constant := 3#0.1#;
\end{lstlisting}
不要用:
\begin{lstlisting}
One_Third_As_Decimal_Approximation : constant := 0.33333_33333_3333;
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
大写或小写字母的一致使用，有助于搜索数字。用下划线来把数字组成熟悉的模式。
与平时使用模式的一致是可读性的一大要素。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
如果一个分数用一个它可以终止的基数进制来表示，而不用一个重复的表示法，就象
上面例子中的\texttt{3\#0.1\#}，在转换到机器的基数时，有可能更精确。
\end{blockindent}

\subsection{大写}
\label{s:readability:cap}
\glentry{准则}
\begin{itemize}
    \item 让保留字和程序的其他组件从视觉上分开。
\end{itemize}
\glentry{具体事例}
\begin{blockindent}
\noindent
\begin{itemize}
    \item[-] 所有保留字用小写 (当用做保留字时)。
    \item[-] 所有其他标识符混用大小写，每个词用一个大写字母开始，
并用下划线分开。
    \item[-] 用大写字母表示缩略语或首写字母缩写 (见自动化注释)。
\end{itemize}
\end{blockindent}
\glentry{范例}
\begin{blockindent}
\noindent
\begin{lstlisting}
...

type Second_Of_Day      is range 0 .. 86_400;
type Noon_Relative_Time is (Before_Noon, After_Noon, High_Noon);

subtype Morning   is Second_Of_Day range 0 .. 86_400 / 2 - 1;
subtype Afternoon is Second_Of_Day range Morning'Last + 2 .. 86_400;

...

Current_Time := Second_Of_Day(Calendar.Seconds(Calendar.Clock));

if Current_Time in Morning then
    Time_Of_Day := Before_Noon;
elsif Current_Time in Afternoon then
    Time_Of_Day := After_Noon;
else
    Time_Of_Day := High_Noon;
end if;

case Time_Of_Day is
    when Before_Noon =>   Get_Ready_For_Lunch;
    when High_Noon   =>   Eat_Lunch;
    when After_Noon  =>   Get_To_Work;
end case;

...
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
视觉上把保留字突出来，让你集中精力在程序结构上，同时有助于扫描某个
标识符。

这里选择的具体事例对于有经验的 Ada 程序员更容易阅读，他不需要保留字
在页面上的突出。 语言的初学者常常发觉保留字的突出有助于他们更
容易的理解控制结构。 因此，学习班的教师和介绍 Ada 语言的书籍可以采用
另一种实现。 \cite{arm95} 选用粗体小写字母来书写所有的保留字。
\end{blockindent}

\glentry{自动化注解}
\begin{blockindent}
Ada 的名字是忽略大小写的。所以象 \texttt{max\_limit}、\texttt{MAX\_LIMIT}、
\texttt{Max\_Limit} 表示一样的对象或实体。只要词之间用下划线分隔，一个好的
代码格式器可以自动从一种风格转换到另一种。

正如\ref{read:abbr} 中所推荐，缩写应该是项目层的。自动化的工具应该允许一个项目
列出它的缩写并格式他们。

\end{blockindent}

\subsection{缩写}
\label{c:readability:abbr}
\glentry{准则}
\begin{itemize}
    \item 如果一个短小的同义词存在，不要使用长词语的缩写。
    \item 用一致的缩写策略。
    \item 不要用有多种解释的缩写。
    \item 缩写必须比原词节省了许多字符，以认可它的使用。
    \item 使用应用领域通用的缩写。
    \item 维护一个缩写列表，只使用列表中的缩写。
\end{itemize}

\glentry{范例}
\begin{blockindent}
用 \texttt{Time\_Of\_Recipt} 比用 \texttt{Recd\_Time} 或 \texttt{R\_Time}
要好。
但是，对于经常需要处理达到军事标准要求的信息格式， \texttt{DOD\_STD\_MSG\_FMT}
是 \texttt{Department\_Of\_Defense\_Standard\_Message\_Format}
\footnote{国防部信息格式标准} 可接受的缩写。
\end{blockindent}

\glentry{原理}
\begin{blockindent}
许多缩写需要结合上下文才不会模糊和不明确。例如，\texttt{Temp} 可以是
临时 (temporary) 或温度 (temperature)。因此，当你需要用到缩写时，要小心的
选择。准则 \ref{c:resuability:abbr} 的原理中就上下文如何影响缩写的使用有
更加彻底的讨论。

因为非常长的变量名会遮蔽程序结构，特别是在缩进的深层嵌套的控制结构中，所以
要尽量让变量名短而有意义。如果可能，尽量的使用短而不用缩写的名字。如果没有
合适的短变量名，那么众所周知的缩写是下一个最好的选择，特别是整个项目都在
使用的标准缩写列表中的名字。

使用 \texttt{rename} 语句，你可以对一个全名建立缩写。这是个很有用的功能，
当在一段本地代码中，一个出现多次的非常长的全名因而可以简化 (参见准则
\cite{c:pratice:rename})。

项目中，一个可接受的缩写列表提供了使用这些缩写的标准语境。
\end{blockindent}

\section{命名约定}
选择一个可以有助表达对象或实体运用的名字。 Ada 允许任意长度的标志符，
只要标志符所有的有效字符(含下划线)可以在一行内。 标志符就是是变量、常量、
程序单元或程序中其他实体的名字。

\subsection{名字}

\glentry{准则}
\begin{itemize}
    \item 尽量选择可以自我说明的名字。
    \item 使用短的同义词而不用缩写 (参见准则\ref{c:readability:abbr})。
    \item 使用应用的名词，但是不要用偏僻的术语。
    \item 避免使用同一个名字来声明不同类型的标识符。
\end{itemize}

\glentry{范例}
\begin{blockindent}
在一个树遍历中，用 \texttt{Left} 在这个语境中已经足够表达它的意思，不用
\texttt{Left\_Branch}。然而，要用 \texttt{Time\_Of\_Day} 而不是 \texttt{TOD}。

数学公式往往用单个字符来命名变量。在程序中，对于数学公式继续这个约定，这样
它们可以提示公式，例如：
\begin{lstlisting}
    A * (X**2) + B * X + C
\end{lstlisting}
当使用子包的时候，如果包、子单元或标志符的名字选的不好，有可能导致与子单元
的可见性冲突。\cite{rational95} (\textsection{8.1})有个因此而变得
很晦涩的代码。
\end{blockindent}

\glentry{原理}
\begin{blockindent}
遵循这些准则的程序很容易被理解。自我说明的名字需要更少的注释说明。经验表明，
如果你的变量名没有过度的长，你可以进一步增加程序的可理解性 (\cite{sch86}, 7)。
上下文和应用域可以提供很多帮助。 数字实体的测量单位是一个子类型名字的来源。

要尽量避免在不同的声明中用同一个名字作为标志符，例如一个对象和一个子包。
在表面上看起来不同的名字空间过度使用同一个标识符，如果这些程序单元要
在一些工作，实际上会导致可见性冲突。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
参见准则\ref{c:resuability:abbr} 有个关于如何用应用领域作为选择缩写的指南。
\end{blockindent}

\subsection{子类型名字}
\label{c:readability:subtype}
\glentry{准则}
\begin{itemize}
    \item 使用单数、泛指的名词作为子类型标志符。
    \item 选用能说明子类型值的标志符。
    \item 对定义可见的访问类型、子界、数组，考虑在子类型标志符中用后缀。
    \item 对私有类型，不要使用针对子类型标志符的构造规则 (例如加后缀)。
    \item 不要使用预定义包中的子类型名字。
\end{itemize}

\glentry{范例}
\begin{blockindent}
\begin{lstlisting}
type Day is
   (Monday,    Tuesday,   Wednesday, Thursday, Friday,
    Saturday,  Sunday);
type Day_Of_Month    is range      0 ..    31;
type Month_Number    is range      1 ..    12;
type Historical_Year is range -6_000 .. 2_500;

type Date is
   record
      Day   : Day_Of_Month;
      Month : Month_Number;
      Year  : Historical_Year;
   end record;
\end{lstlisting}
特别指出，\texttt{Day} 应该优先于 \texttt{Days} 或 \texttt{Day\_Type} 的使用。

标志符 \texttt{Historical\_Year} 也许看起来象特指，但实际是泛指的，形容词
\texttt{historical} 说明了范围的限制。
\begin{lstlisting}
------------------------------------------------------------------------
procedure Disk_Driver is
   -- In this procedure, a number of important disk parameters are
   -- linked.
   Number_Of_Sectors  : constant :=      4;
   Number_Of_Tracks   : constant :=   200;
   Number_Of_Surfaces : constant :=    18;
   Sector_Capacity    : constant := 4_096;

   Track_Capacity   : constant := Number_Of_Sectors  * Sector_Capacity;
   Surface_Capacity : constant := Number_Of_Tracks   * Track_Capacity;
   Disk_Capacity    : constant := Number_Of_Surfaces * Surface_Capacity;

   type Sector_Range  is range 1 .. Number_Of_Sectors;
   type Track_Range   is range 1 .. Number_Of_Tracks;
   type Surface_Range is range 1 .. Number_Of_Surfaces;

   type Track_Map   is array (Sector_Range)  of ...;
   type Surface_Map is array (Track_Range)   of Track_Map;
   type Disk_Map    is array (Surface_Range) of Surface_Map;
begin -- Disk_Driver
   ...
end Disk_Driver;
------------------------------------------------------------------------
\end{lstlisting}
后缀 \texttt{\_Capacity}、\texttt{\_Range}和\texttt{\_Map} 帮助定义了上述
子类型的目的，避免了寻找抽象 sector, track 和 surface 的同义词。没有了这些
后缀，对每一个抽象你需要三个不同的名字来说明每个简洁后缀名中所描述的概念。
这个建议只对那些可见的子类型。对于私有类型，应该被赋予一个能反应抽象代表的
好名字。
\end{blockindent}

\glentry{原理}
\begin{blockindent}
当使用这个风格和建议的对象标志符风格时，程序代码更相似于英文
(参见准则\ref{c:readability:obj})。另外，这个风格和语言预定义的标志符名字
保持了一致。它们并没有被命名为 \texttt{Integers}, \texttt{Booleans},
\texttt{Interger\_Type} 或 \texttt{Boolean\_Tyoe}。

但是，如果使用预定义包中的子类型名，当子类型出现在某处而没有包限定的话，
一定会弄糊涂程序员。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
这个风格准则试图和 \cite{arm95} 在对术语 ``type (类型）''
和 ``subtype (子类型) 名字'' 的使用上保持一致。通常 ``类型'' 指一个抽象的
概念，如在类型声明中，而 ``子类型'' 指在实际声明中赋予抽象概念的名字。所以
在 \cite{arm83} 中的类型现在是子类型。
\end{blockindent}

\subsection{对象名字}
\glentry{准则}
\begin{itemize}
    \item 对于逻辑对象 使用判断语或从属的名字。
    \item 使用单数、泛指的名词作为对象标志符。
    \item 选用能说明对象执行时的值的标识符。
    \item 使用单数、泛指的名词作记录的组件。
\end{itemize}

\glentry{范例}
\begin{blockindent}
\noindent
非逻辑对象:
\begin{lstlisting}
  Today           : Day;
  Yesterday       : Day;
  Retirement_Date : Date;
\end{lstlisting}
逻辑对象:
\begin{lstlisting}
  User_Is_Available : Boolean; -- predicate clause
  List_Is_Empty     : Boolean; -- predicate clause
  Empty             : Boolean; -- adjective
  Bright            : Boolean; -- adjective
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
使用针对对象的名词建立了理解对象值的语境，这正是有关子类型命名的一点
(参见准则\ref{c:readability:subtype})。这种风格下，对象的声明很像英文。
例如，上面的第一个声明可读为着者``Today is Day''。

泛指的名词用在记录的组件命名，因为一个记录的对象名会提供理解组件的语境。
因此，下面的组件可理解为``the year of retirement (退休的年份)'':
\begin{lstlisting}
    Retirement_Date.Year
\end{lstlisting}
把对象类型和语言联系起来，可以是代码看起来更像文本。例如，因为选取了恰当
的名字，下面的代码不需要注释:
\begin{lstlisting}
    if List_Is_Empty then
       Number_Of_Elements := 0;
    else
       Number_Of_Elements := Length_Of_List;
    end if;
\end{lstlisting}
\end{blockindent}

\glentry{注解}
\begin{blockindent}
如果很难找到一个合适的名词来声明一个对象在整个执行期间的值，这个对象很可能
服务于多个目的。这种情况下，应该用多个对象。
\end{blockindent}

\subsection{标签类型和相对应包的命名}
\label{c:readability:tag}
\glentry{准则}
\begin{itemize}
    \item 使用一致的命名约定来对标签类型和相对应包命名。
\end{itemize}

\glentry{具体事例}
\begin{blockindent}
命名约定点燃了``信仰战争''; 因此，这里展现展了两种具体实例。第一种实例集成了
面向对象的使用特性。除了两种特殊情况，它在声明种使用同一命名约定，不管是否
用到了面向对象的特性:
\begin{itemize}
    \item[-] 对标签类型和子类型的命名一致
(参见准则\ref{c:readability:subtype})。
    \item[-] 对于导出会有多个实现的抽象 (参见准则\ref{c:oof:tagged:abstract}) 的包，
使用前缀 \texttt{Abstract\_}。
    \item[-] 对于提供可以和主要抽象``mixed in (混合)''的功能单元的包，
用后缀 \texttt{\_Mixin}。
\end{itemize}
第二种实现用特殊的名字和后缀来突出面向对象的特性的使用:
\begin{itemize}
    \item[-] 用代表的对象来命名类包，不用后缀 (\cite{rosen95})。
    \item[-] 用混合包所代表的一面类命名，用后缀 \texttt{\_Facet}
	     (\cite{rosen95})。
    \item[-] 用 \texttt{Instance} 命名主标签类型。
    \item[-] 沿用某个类型的声明，对应的类程子类型用 \texttt{Class}。
\end{itemize}
\end{blockindent}

\glentry{范例}
\begin{blockindent}
下面的两组从\cite{rational95} (\textsection{4.4.4}, \textsection{4.6.2})
中选出的例子，使用了第一种命名约定。

在第一组的例子种，假设 \texttt{Set\_Element} 已经在别的地方声明过了:
\begin{lstlisting}
  package Abstract_Sets is
     type Set is abstract tagged private;
     -- empty set
     function Empty return Set is abstract;
     -- build set with 1 element
     function Unit (Element: Set_Element) return Set is abstract;
     -- union of two sets
     function Union (Left, Right: Set) return Set is abstract;
     -- intersection of two sets
     function Intersection (Left, Right: Set) return Set is abstract;
     -- remove an element from a set
     procedure Take (From      : in out Set;
                      Element :     out set_Element) is abstract;
     Element_Too_Large : exception;
  private
     type Set is abstract tagged null record;
  end Abstract_Sets;
  with Abstract_Sets;
  package Bit_Vector_Sets is     -- one implementation of set abstraction
     type Bit_Set is new Abstract_Sets.Set with private;
     ...
  private
     Bit_Set_Size : constant := 64;
     type Bit_Vector is ...
     type Bit_Set is new Abstract_Sets.Set with
         record
            Data : Bit_Vector;
         end record;
  end Bit_Vector_Sets;
  with Abstract_Sets;
  package Sparse_Sets   -- alternate implementation of set abstraction
     type Sparse_Set is new Abstract_Sets.Set with private;
     ...
  private
     ...
  end Bit_Vector_Sets;
\end{lstlisting}

第二组例子是个支持视窗系统的混合包，应用了混合包的命名约定:
\begin{lstlisting}
  -- assume you have type Basic_Window is tagged limited private;
  generic
     type Some_Window is abstract new Basic_Window with private;
  package Label_Mixin is
     type Window_With_Label is abstract new Some_Window with private;
     ...
  private
     ...
  end Label_Mixin;
  generic
     type Some_Window is abstract new Basic_Window with private;
  package Border_Mixin is
     type Window_With_Label is abstract new Some_Window with private;
     ...
  private
     ...
  end Border_Mixin;
\end{lstlisting}

下面的例子应用了第二种命名约定，是\cite{rosen95}中讨论的:
\begin{lstlisting}
  package Shape is
     subtype Side_Count is range 0 .. 100;
     type Instance (Sides: Side_Count) is tagged private;
     subtype Class is Instance'Class;
     . . .
     -- operations on Shape.Instance
  private
     . . .
  end Shape;

  with Shape; use Shape;
  package Line is
     type Instance is new Shape.Instance with private;
     subtype Class is Instance'Class;
     . . .
     -- Overridden or new operations
  private
     . . .
  end Line;
  with Shape; use Shape;
  generic
     type Origin is new Shape.Instance;
  package With_Color_Facet is
     type Instance is new Origin with private;
     subtype Class is Instance'Class;
     -- operations for colored shapes
  private
     . . .
  end With_Color_Facet;
  with Line; use Line;
  with With_Color_Facet;
  package Colored_Line is new With_Color_Facet (Line.Instance);
\end{lstlisting}
声明看起来会是这样:
\begin{lstlisting}
  Red_Line : Colored_Line.Instance;
  procedure Draw (What : Shape.Instance);
\end{lstlisting}
上述方案在用全名或用 \texttt{use} 语句都工作。只要你用同样的名字来
表示所有的类型和类程类型，没有确认的名字总是相互隐藏。因此，编译器会
要求你使用全名称来解决用\texttt{use} 语句所带来的多义性 \cite{rosen95}。
\end{blockindent}

\glentry{原理}
\begin{blockindent}
你应采用一个一致、可读、传达抽象意图的命名方案。理想状况下，命名方案应统一
处理从不同方法用标签类型生成类别。如果命名约定太死板，从可读性的角度看，
那你写的代码片段会不自然。对于推导或通用混合的(\ref{c:oof:mul-inh:tech}
类型夸展使用相近的命名约定，对象和过程的声明变得可读。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
类的命名约定在面向对象的抽象和其他抽象间画了一条分界线。工程师们已经用 Ada 83
(\cite{arm83}) 定义抽象数据类型有十多年了，你并不想只是为了使用某个类型的
类型扩展而改变命名约定。你必须考虑所有在程序里使用的抽象中，继承有多重要。
一般情况下，如果你喜欢突出抽象，而不是实现抽象的机制 (例如：继承、类型夸展
以及多态), 也许你并不想强加这么一个苛刻的命名约定。一个平滑的从开发没有继承的
抽象过渡到开发有继承的抽象的命名约定，不会影响质量。

如果你选用了突出使用面向对象特性的命名约定，而之后有想改变声明到不用
面向对象特性，这个改变也许会代价高昂。你必须更换所有出现的名字，并且不引入
别的错误。如果你选用了一个命名约定，禁止使用前、后缀来描述声明的特性，你
失去了一个传递声明使用意图的机会。
\end{blockindent}

\subsection{程序单元的名字}
\glentry{准则}
\begin{itemize}
    \item 用行动的动词来命名过程和入口。
    \item 用判断式的子句命名逻辑函数。
    \item 用名词命名非逻辑函数。
    \item 给包的名字要暗含比子程序高级的组构。通常来说，这都是描述供应的抽象的
名词。
    \item 给任务命名隐含活动的实体。
    \item 用描述被保护数据的名词来命名保护单元。
    \item 把通用子程序当做非通用子程序来命名。
    \item 把通用包当做非通用包来命名。
    \item 通用名字应该比例示的名字更广泛。
\end{itemize}

\glentry{范例}
\begin{blockindent}
\noindent
下面是些一个Ada程序的构成元素的名字样本：

过程名字样本:
\begin{lstlisting}
    procedure Get_Next_Token -- get is a transitive verb
    procedure Create         -- create is a transitive verb
\end{lstlisting}

逻辑函数名字样本:
\begin{lstlisting}
    function Is_Last_Item -- predicate clause
    function Is_Empty     -- predicate clause
\end{lstlisting}

非逻辑函数名字样本:
\begin{lstlisting}
    function Successor -- common noun
    function Length    -- attribute
    function Top       -- component
\end{lstlisting}

包名字样本:
\begin{lstlisting}
    package Terminals is     -- common noun
    package Text_Routines is -- common noun
\end{lstlisting}

被保护对象样本:
\begin{lstlisting}
    protected Current_Location is -- data being protected
    protected type Guardian is    -- noun implying protection
\end{lstlisting}

任务名字样本:
\begin{lstlisting}
    task Terminal_Resource_Manager is -- common noun that shows action
\end{lstlisting}

下面这段代码样本，因为使用语言部分命名约定而呈现的清晰结果:
\begin{lstlisting}
    Get_Next_Token(Current_Token);
    case Current_Token is
       when Identifier =>          Process_Identifier;
       when Numeric    =>          Process_Numeric;
    end case; -- Current_Token
    if Is_Empty(Current_List) then
       Number_Of_Elements := 0;
    else
       Number_Of_Elements := Length(Current_List);
    end if;
\end{lstlisting}

当包和她的子程序一起被指定，能得到非常有描述性的代码:
\begin{lstlisting}
    if Stack.Is_Empty(Current_List) then
       Current_Token := Stack.Top(Current_List);
    end if;
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
当使用这些命名约定，代码会易于理解，读起来也象自然语言。当动词
用于行动，如子程序，名词用于对象，例如子程序处理的数据，写出的
代码更容易阅读和理解。这里模仿了读者熟悉的交流媒体。一段程序模拟
了现实中的情况，使用这样的约定，减少了阅读和理解程序种的翻译次数。
某种意义上，你选择的名字反应了从电脑硬件到应用要求的抽象级别。

有关标签类型相关的包中，特殊意义的后缀使用，
参见准则\ref{c:readability:tag} 。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
当前，对于任务的进入的命名，有些相互矛盾的命名约定。有些程序员和
设计员提倡任务的进入使用和子程序一样的命名约定，模糊了程序中包含了
任务的事实。他们的理由是如果任务用包重新实现或反之，名字不需要被
替换。其他人则喜欢尽可能的突出任务存在的事实，以保证能分辨出任务的
存在和它所带来的假设额外开销。 可以根据项目侧重点的不同来选择使用
适当的命名约定。
\end{blockindent}

\subsection{常量和命名的数字}
\glentry{准则}
\begin{itemize}
    \item 尽量用符号的名字而不是文字。
    \item 对于数学常量 \texttt{Pi} 和 \texttt{e},
用预定义的常量 \texttt{Ada.Numerics.Pi} 和 \texttt{Ada.Numerics.e}。
    \item 用常量而不是变量来表示常数。
    \item 当一个值是某个类型所特有的或这个值必须静止，用常量。
    \item 尽量用命名的数字，而不是常量。
    \item 用命名的数字来代替文字的数字，它的类型或属性是真正的通用的。
    \item 对于对象的值在确立之后不能改变的，用常量(\cite{united87})。
    \item 用静止表达式来表示符号间的关系。
    \item 用线性的独立的文字组。
    \item 尽量用属性 \texttt{'First} 或者 \texttt{'Last} 而不是文字。
\end{itemize}


\glentry{范例}
\begin{lstlisting}
    3.14159_26535_89793                               -- literal
    Max_Entries : constant Integer      := 400;       -- constant
    Avogadros_Number : constant := 6.022137 * 10**23; -- named number
    Avogadros_Number / 2                              -- static expression
    Avogadros_Number                                  -- symbolic value
\end{lstlisting}

声明 \texttt{Pi} 作为一个被命名的数字 (假设有对\cite{arm95}[\textsection{}A.5]
中的预定义包 \texttt{Ada.Numerics}使用\texttt{with} 语句), 它可以被下面的
赋值语句用符号引用:
\begin{lstlisting}
    Area := Pi * Radius**2; -- if radius is known.
\end{lstlisting}
而不用这样:
\begin{lstlisting}
    Area := 3.14159 * Radius**2; -- Needs explanatory comment.
\end{lstlisting}

另外，\texttt{ Ada.Characters.Latin\_1.Bel} 比 \texttt{Character'Val(8\#007\#)}
更具表达性。

常量和被命名数字的清晰度，可以因使用其他的常量和被命名数字而提高。例如:
\begin{lstlisting}
    Bytes_Per_Page   : constant := 512;
    Pages_Per_Buffer : constant := 10;
    Buffer_Size      : constant := Pages_Per_Buffer * Bytes_Per_Page;
\end{lstlisting}
比以下的代码更能自我说明和维护:
\begin{lstlisting}
    Buffer_Size : constant := 5_120; -- ten pages
\end{lstlisting}

下面的文字应该用常量:
\begin{lstlisting}
    if New_Character = '$' then -- "constant" that may change
    ...
    if Current_Column = 7 then  -- "constant" that may change
\end{lstlisting}


\glentry{原理}
\begin{blockindent}
用标识符而不是文字，使表达式的目的明确，也减少了注释的需要。含有数字文字
表达式的常量更安全，因为他们不需要人工去计算。他们也比单独的数字文字有
启发作用，因为有更多的机会植入解释性的名字。常量声明的清晰度可以因为在
静态表达式中使用其他相关的常量来定义新的常量而提高。这并不会降低效率，
因为被命名数字的静态表达式在编译时就被计算出来了。

一个常量是有类型的。一个被命名数字只能是通用类型: \texttt{universal\_integer}
或 \texttt{universal\_real}。强类型只对常量起作用，对被命名数字或文字
不起作用。被命名数字允许编译器产生比常量更有效的代码，编译时做更完全
的错误检查。如果文字包含了许多个数字 (如上面例子中的 \texttt{Pi})，使用
标志符减少了击键错误。如果有击键错误，他们也更容易在检阅或编译时被指出。

文字的独立意味着少数几个文字的使用不会互相依赖，而任何常量和被命名值之间的
关系由静态表达式表现。线性独立的文字值，给出这样一个机会，如果这个文字值变了，
所有依赖于这个文字的被命名数字的值都会自动被改变。

参见准则\ref{c:function-struct:func}中更多有关选择无参数函数和常量的准则。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
有某些灰色地带，数字本身比名字更能自我说明。这是某些特殊的应用，通常在数字
是大家都熟悉的，值不会变的情况下发生:
\begin{lstlisting}
    Fahrenheit := 32.0 + (9.0 * Celsius) / 5.0;
\end{lstlisting}
\end{blockindent}

\subsection{异常}
\glentry{准则}
\begin{itemize}
    \item 用异常所代表的问题种类命名。
\end{itemize}

\glentry{范例}
\begin{lstlisting}
    Invalid_Name: exception;
    Stack_Overflow: exception;
\end{lstlisting}

\glentry{原理}
\begin{blockindent}
用检测到的问题作异常的名字，提高了代码的可读性。你应该尽量精确的命名你的
异常，这样代码的维护者会理解为什么异常会发生。一个正确命名的异常，应该对
使用客户有意义。
\end{blockindent}

\subsection{构造器}
\glentry{准则}
\begin{itemize}
    \item 使用前缀 \texttt{New}, \texttt{Make} 或 \texttt{Create}
命名构造器 (这里指创造或初始化对象的操作)。
    \item 对于含有构造器的子包，用可以标示内容的名字。
\end{itemize}

\glentry{具体事例}
\begin{itemize}
    \item[-] 命名一个含有构造器的子包 \texttt{<whatever>.Constructor}。
\end{itemize}

\glentry{范例}
\begin{lstlisting}
    function Make_Square (Center : Cartesian_Coordinates;
			  Side   : Positive)
      return Square;
\end{lstlisting}

\glentry{原理}
\begin{blockindent}
在构造器名字中使用前缀 \texttt{New}, \texttt{Make} 或 \texttt{Create}，
令它的目的清晰。你也许希望限制前缀 \texttt{New} 只用于返回存取值的构造器，
因为这个前缀暗示内部使用了分配符。

将所有的构造器都放在一个子包中是很有用的组织原则，即使他们返回存取值。

有关 Ada 的构造器的信息，参见准则\ref{c:oof:tagged:constructor}。
\end{blockindent}

\section{注释}
源代码中的注释是个有争论的议题。对于注释是否会提高阅读性，反对和赞成的都有
依据。实践中，注释最大的问题是，当代码改变时，人们经常忘记改变对应的注释，
这导致注释会误导读者。注释应该保留给代码无法表达的内容，以及突出某些准则
被违背的原因。如果可能，代码中的对象和单元应该使用可以自我说明的名字，简单、
容易理解的程序结构也应该被使用，这样就需要非常少的注释。为了选择 (和键入)
合适的名字所做的额外努力，以及对设计干净和可理解的程序结构的额外思考是
完全应该的。

使用注释来陈述代码的意图。概述代码的注释有助于维护的程序员看到整体意图。代码
本身是如何实现的详细内容，不应该在注释中改述。

注释应该减至最少。他们应该提供 Ada 程序中无法表述的必须内容，侧重于代码结构，
以及引起注意到故意和必须违反准则的地方。注释的出现应该让人注意到例示的真正
问题或者补充示例程序中的不足。

维护的程序员要知道不相邻代码间的因果交互关系，以便对程序有一个差不多
的全局观。他们一般都是通过人工模拟部分代码来得到这些信息。注释应该足够支持
这个过程 (\cite{soloway86})。

本节呈现了有关怎样写好的注释的通用准则。之后又定义了几个不同类型的注释，
每种类型都有对应的使用准则。这些类型有文件头、程序单元规约头、程序单元主体头、
数据注释、语句注释和标记注释。

\subsection{一般注释}
\glentry{准则}
\begin{itemize}
    \item 尽可能写清晰的代码来减少注释的需求。
    \item 绝不要在注释里重复可以从代码中轻易获得的信息。
    \item 在需要注释的地方，要写得简明和完整。
    \item 注释中要用正确的语法和拼写。
    \item 注释要在视觉上和代码区分出来。
    \item 把注释放在数据头上，这样相关的信息可以被自动工具提取出来。
\end{itemize}

\glentry{原理}
\begin{blockindent}
结构和函数写得好的代码，没有注释也很清楚。对于晦涩的或结构糟糕的代码，无论
有没有注释都很难令人理解、维护或重用。\emph{坏代码应该被改进而不是解释}。
阅读代码本身是唯一可以绝对确定代码做了些什么的方法，所以代码应该尽可能写
得可读。

在注释中重复代码中的信息是个很不好的主意。有几个原因，首先，这不必要，
而且降低效率。第二，代码在不断的改动，很难正确地维护重复的信息。当代码
被改动时，它需要通过编译和测试来确定其正确性。但是，没有一种自动的机制
可以保证注释能正确反映代码的改变。经常，注释中重复的信息在首次代码改动后
就变得过时，但一直存在于软件的生命周期中。第三，当有关整个系统的注释
用一个子系统的作者的有限眼光来写，这个注释通常从一开始就不正确。

注释在揭示从代码中很难或不可能得到的信息时很有必要。本书后续的章节中
会有这样的注释。完全而简明的呈现所要求的信息。

注释的目的是帮助读者理解代码。拼写错误、不合语法、岐义或不完整的注释违背
了这个目的。如果一个注释值得加入，那就值得正确的加入来增大它的有用性。

用缩进、和头组合在一起或者用虚线来突出注释，使得注释从视觉上和代码区分出来，
这很有用，因为它使代码变得容易阅读。本书的后续章节会详细说明这点。
\end{blockindent}

\glentry{自动化注解}
\begin{blockindent}
有关在注释中存放多余信息的准则是只在人工注释中应用。有些工具可以自动维护
代码的相关信息 (例如，调用的单元、被调用的单元、交叉引用信息和修订历史等等)，
这些信息存放在和代码同一文件的注释中。其他工具可以读取注释，但不更新它们，
从注释中的信息可以自动生成详细的设计文档或其他报告。

这些工具的使用，鼓励或要求你构造头注释，这样它们才可以被自动提取或更新。
注意，自动改变文件中的注释的工具，只有足够的经常运行才有用。自动生成的
过时信息比人工的过时信息更危险，因为读者更相信它们。

配置管理工具对修订历史的维护更加准确和完整。在没有工具的支持下，工程师
修改了代码而忘了更新修订历史是很常见的事。如果你的配置管理工具可以在源
文件中以注释形式维护修订历史，不管在格式或位置上如何折衷，都要利用这个
功能。在文件尾部附加一个完整的修订历史，要比在文件头有一个格式很好但不
完整的历史要好。
\end{blockindent}

\subsection{文件头}
\glentry{准则}
\begin{itemize}
    \item 在每一个源文件中放一个文件头。
    \item 在文件头中，放入所有者、责任和历史信息。
\end{itemize}

\glentry{具体事例}
\begin{itemize}
    \item[-] 在文件头中放版权通告。
    \item[-] 在文件头中放作者名字和部门。
    \item[-] 在文件头中放修订历史，包括每个改动的概要，日期和修改的人。 
\end{itemize}

\glentry{范例}
\begin{blockindent}
\begin{lstlisting}
------------------------------------------------------------------------
--      Copyright (c) 1991, Software Productivity Consortium, Inc.
--      All rights reserved.
-- Author: J. Smith
-- Department:System Software Department
-- Revision History:
--   7/9/91 J. Smith
--     - Added function Size_Of to support queries of node sizes.
--     - Fixed bug in Set_Size which caused overlap of large nodes.
--   7/1/91 M. Jones
--     - Optimized clipping algorithm for speed.
--   6/25/91 J. Smith
--     - Original version.
------------------------------------------------------------------------
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
如果你想要确定保护你对软件的权利，每一个源文件都应该有所有者信息。另外，
为了有更高的可见性，它应该是文件中的第一项。

责任和修订信息也应该在每个文件中，这方便未来的维护者。这些头信息是维护者
最相信的信息，因为它们累积。它们不会演变。没有修改文件作者名字或修订历史
的需要，修订历史应该反映每次改动而更新。最糟的，也就是它不完整，它很少会
出错。而且，一个单元的历史上修改的次数和频率，修改的人的个数都是很好的
关于设计的实现完整性的指示器。

除了作者名字，有关如何找到最初作者的信息也应该包含在文件头里，如果有问题
产生，这有助于维护者找到原作者。但是，详细的信息，象电话号码、邮政地址、
办公室号码、电脑帐号用户名则太易变动而不太有用。最好记录下作者写代码时
工作的部门。这个信息在作者欢办公室、部门甚至离开公司后还是有用的，因为
这个部门很有可能还对原来版本负责。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
现代的配置管理系统，直接从文件头注释中截取版本历史是多余的。管理系统维护
着一个从内容上看更可靠和一致的修改历史。某些系统可以重现一个单元的早期版本。
\end{blockindent}

\subsection{程序单元规约头}

\glentry{准则}
\begin{itemize}
    \item 在每一个程序单元规约都放一个头。
    \item 在程序单元规约头中放上用户使用要求信息。
    \item 除了单元名字，不要在头中重复规约中的信息。
    \item 解释单元做些什么，而不是怎样和为什么这么做。
    \item 描述这个程序单元的完整接口，包括它能抛出的任何异常和任何可以造成
全局影响的地方。
    \item 不要包括这个单元如何适用于包含的软件系统的信息。
    \item 描述单元的性能特性 (时间、空间)。
\end{itemize}

\glentry{具体事例}
\begin{itemize}
    \item[-] 在头中放入程序名。
    \item[-] 简单说明程序单元的目的。
    \item[-] 对于包，描述可见子程序相互之间的影响和应当怎样一起使用它们。
    \item[-] 列出单元抛出的所有异常。
    \item[-] 列出单元所有的全局影响。
    \item[-] 列出单元的前置条件和后置条件。
    \item[-] 列出单元的激活的隐藏任务。
    \item[-] 不要列出子程序的参数名字。
    \item[-] 不要只为列出而列出子程序的名字。
    \item[-] 不要列出单元中使用的其它单元的名字。
    \item[-] 不要列出使用本单元的其它单元的名字。
\end{itemize}

\glentry{范例}
\begin{lstlisting}
------------------------------------------------------------------------
-- AUTOLAYOUT
-- Purpose:
--    This package computes positional information for nodes and arcs
--    of a directed graph. It encapsulates a layout algorithm which is
--    designed to minimize the number of crossing arcs and to emphasize
--    the primary direction of arc flow through the graph.
-- Effects:
--    - The expected usage is:
--      1. Call Define for each node and arc to define the graph.
--      2. Call Layout to assign positions to all nodes and arcs.
--      3. Call Position_Of for each node and arc to determine the
--         assigned coordinate positions.
--    - Layout can be called multiple times, and recomputes the
--      positions of all currently defined nodes and arcs each time.
--    - Once a node or arc has been defined, it remains defined until
--      Clear is called to delete all nodes and arcs.
-- Performance:
--    This package has been optimized for time, in preference to space.
--    Layout times are on the order of N*log(N) where N is the number
--    of nodes, but memory space is used inefficiently.
------------------------------------------------------------------------
package Autolayout is
   ...
   ---------------------------------------------------------------------
   -- Define
   -- Purpose:
   --    This procedure defines one node of the current graph.
   -- Exceptions:
   --    Node_Already_Defined
   ---------------------------------------------------------------------
   procedure Define
	  (New_Node : in      Node);
   ---------------------------------------------------------------------
   -- Layout
   -- Purpose:
   --    This procedure assigns coordinate positions to all defined
   --    nodes and arcs.
   -- Exceptions:
   --    None.
   ---------------------------------------------------------------------
   procedure Layout;
   ---------------------------------------------------------------------
   -- Position_Of
   -- Purpose:
   --    This function returns the coordinate position of the
   --    specified node. The default position (0,0) is returned if no
   --    position has been assigned yet.
   -- Exceptions:
   --    Node_Not_Defined
   ---------------------------------------------------------------------
   function Position_Of (Current : in Node)
	 return Position;
   ...
end Autolayout;
\end{lstlisting}

\glentry{原理}
\begin{blockindent}
程序单元规约注释头的目的是帮助用户理解怎样使用本单元。从阅读规范和注释头中，
用户应给知道有关如何使用单元的所有必须的信息。它不应该还需要阅读单元主体。所以，
每一个单元规约都应该有一个注释头，每个头应该包含所有没有在规范中表达的使用
信息。这些信息应包括单元之间的相互影响，对共享资源的影响，异常的抛出，以及
时间、空间的特性。这些信息都不可能从 Ada 的程序单元规约中得到。

当你在注释头中重复可以从规约中读得的信息，这些信息在维护过程中会趋于不准确。
例如，当定义一个过程时，列出所有的参数名字、模式或子类型并无意义，这些信息
在规约中都有。同样的，不要在注释头中列出包中所有的子程序，除非这是有关子程序
的重要声明中必须的。

不要在注释头中加入用户不需要的信息。特别是不要包含程序单元如何完成它的功能
或为什么运用了某个特定的算法。这些信息应该藏在程序体内，以保持单元的抽象性。
如果用户知道这些信息，并由此作出一些决定，当这些信息改变后，程序也许会因此而
出错。

当叙述单元的目的时，避免提及系统内其它部分。``本单元做 ... ...'' 比 ``本单元由
Xyz 调用做 ... ...'' 要好。程序单元应该编写为不必知道和搭理谁调用它，这使得
单元更具广泛性和重用性。另外，有关其它单元的信息很容易在维护过程中变得过时和
不正确。

单元的性能特性 (时间和空间) 应该包含在性息中。许多这些信息并没有在 Ada 规约中
出现，但是用户需要。要整合单元到系统中，用户需要知道资源(CPU、内存，等等)的
使用状况。特别重要的是当一个子程序调用会激活一个程序体中隐藏的任务，这个任务
有可能在子程序结束后还在继续消耗资源。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
某些项目把大多数的注释都放在程序单元结尾而不是开头。他们的依据是单元只编写一
次，而被阅读许多次，长长的头注释让规约的开始很难被找到。
\end{blockindent}

\glentry{异例}
\begin{blockindent}
当一组程序单元相互有紧密联系或者很容易理解，可以用一个头注释来解释这一组单元。
例如，emph{最大}和\emph{最小}函数;Sin, Cos 和 Tan 函数; 或者一个包中的针对
某一对象的一组查询属性的函数。当某组中的每一个函数都可以抛出相同的异常，这就
更加需要一个注释。
\end{blockindent}

\subsection{程序单元主体注释头}
\glentry{准则}
\begin{itemize}
    \item 单元维护者需要的信息。
    \item 解释单元如何以及为什么进行它的功能，而不是单元做什么。
    \item 不要重复从代码中就能轻易得到的信息(除了单元名字)。
    \item 不要重复规约头中有的信息(除了单元名字)。
\end{itemize}

\glentry{具体事例}
\begin{itemize}
    \item[-] 放上程序单元名字。
    \item[-] 记录移植问题。
    \item[-] 概述复杂的算法。
    \item[-] 记录重大或有争议的实现决定原因。
    \item[-] 记录丢弃的替代实现，和丢弃的原因。
    \item[-] 记录预期的改变，特别是部分工作已经完成，以便改变更容易完成。
\end{itemize}

\glentry{范例}
\begin{lstlisting}
------------------------------------------------------------------------
-- Autolayout
-- Implementation Notes:
--   - This package uses a heuristic algorithm to minimize the number
--     of arc crossings.  It does not always achieve the true minimum
--     number which could theoretically be reached.  However it does a
--     nearly perfect job in relatively little time.  For details about
--     the algorithm, see ...
-- Portability Issues:
--   - The native math package Math_Lib is used for computations of
--     coordinate positions.
--   - 32-bit integers are required.
--   - No operating system specific routines are called.
-- Anticipated Changes:
--   - Coordinate_Type below could be changed from integer to float
--     with little effort.  Care has been taken to not depend on the
--     specific characteristics of integer arithmetic.
------------------------------------------------------------------------
package body Autolayout is
   ...
   ---------------------------------------------------------------------
   -- Define
   -- Implementation Notes:
   --   - This routine stores a node in the general purpose Graph data
   --     structure, not the Fast_Graph structure because ...
   ---------------------------------------------------------------------
   procedure Define
	 (New_Node : in     Node) is
   begin
      ...
   end Define;
   ---------------------------------------------------------------------
   -- Layout
   -- Implementation Notes:
   --   - This routine copies the Graph data structure (optimized for
   --     fast random access) into the Fast_Graph data structure
   --     (optimized for fast sequential iteration), then performs the
   --     layout, and copies the data back to the Graph structure.  This
   --     technique was introduced as an optimization when the algorithm
   --     was found to be too slow, and it produced an order of
   --     magnitude improvement.
   ---------------------------------------------------------------------
   procedure Layout is
   begin
      ...
   end Layout;
   ---------------------------------------------------------------------
   -- Position_Of
   ---------------------------------------------------------------------
   function Position_Of (Current : in     Node)
	 return Position is
   begin
      ...
   end Position_Of;
   ...
end Autolayout;
\end{lstlisting}

\glentry{原理}
\begin{blockindent}
单元主体注释头的目的是帮助维护者理解单元的实现，包括各种不同技术的
替代妥协。要保证记录实现过程中所有的决定，避免维护者犯同样的错误。
对维护者最有用的一种注释是有关某种改变为什么不行的描述。

注释头也是记录移植性考虑的好地方。维护者也许需要移植程序到不同的
环境，一个不可移植的特性列表对此帮助很多。另外，收集和记录移植性
问题本身，令注意力集中在这些问题上，那从一开始就会产生可移植性的
更高的代码。

如果复杂的算法很难从阅读代码中理解，在注释中加入算法的概述，但不要
仅仅改写一下代码。那样的重复的内容没有必要也很难维护。同样的，不要
重复规约头中的信息。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
经常的，一个程序单元可以自我解释，不需要一个解释头。这样的情况，
完全乎略掉解释头，就像上面的 Position\_Of。记住要确定你乎略的信息
真的不包含信息。例如，看看下面两个头的不同:
\begin{lstlisting}
-- Implementation Notes: None.
\end{lstlisting}
和:
\begin{lstlisting}
-- NonPortable Features: None.
\end{lstlisting}
第一条信息是作者对维护者说:``我想不到任何其他东西告诉你。'' 而第二条信息
意思是``我保证本单元完全可移植性。''
\end{blockindent}

\subsection{数据注释}
\glentry{准则}
\begin{itemize}
    \item 除非他们的名字能自我解释，否则注释所有的数据类型，对象和异常。
    \item 从语意上注释复杂的指针类的数据结构。
    \item 注释数据对象之间存在的关系。
    \item 乎略名字中包含的信息。
    \item 对于标签类型，如果你期望它的特殊化(例如衍生类型)复写某些重分派
    操作，注释重分派的信息。
\end{itemize}

\glentry{范例}
\begin{blockindent}
可以用目的来对对象分组，那注释为：
\begin{lstlisting}
...
---------------------------------------------------------------------
-- Current position of the cursor in the currently selected text
-- buffer, and the most recent position explicitly marked by the
-- user.
-- Note:  It is necessary to maintain both current and desired
--        column positions because the cursor cannot always be
--        displayed in the desired position when moving between
--        lines of different lengths.
---------------------------------------------------------------------
Desired_Column : Column_Counter;
Current_Column : Column_Counter;
Current_Row    : Row_Counter;
Marked_Column  : Column_Counter;
Marked_Row     : Row_Counter;
\end{lstlisting}
应该注释异常被抛出的条件:
\begin{lstlisting}
---------------------------------------------------------------------
-- Exceptions
---------------------------------------------------------------------
Node_Already_Defined : exception;   -- Raised when an attempt is made
				    --|   to define a node with an
				    --|   identifier which already
				    --|   defines a node.
Node_Not_Defined     : exception;   -- Raised when a reference is
				    --|   made to a node which has
				    --|   not been defined.
\end{lstlisting}
下面是个更加复杂的例子，包括了多个记录，访问类型来组成一个复杂的数据结构:
\begin{lstlisting}
---------------------------------------------------------------------
-- These data structures are used to store the graph during the
-- layout process. The overall organization is a sorted list of
-- "ranks," each containing a sorted list of nodes, each containing
-- a list of incoming arcs and a list of outgoing arcs.
-- The lists are doubly linked to support forward and backward
-- passes for sorting. Arc lists do not need to be doubly linked
-- because order of arcs is irrelevant.
-- The nodes and arcs are doubly linked to each other to support
-- efficient lookup of all arcs to/from a node, as well as efficient
-- lookup of the source/target node of an arc.
---------------------------------------------------------------------
type Arc;
type Arc_Pointer is access Arc;
type Node;
type Node_Pointer is access Node;
type Node is
   record
      Id       : Node_Pointer;-- Unique node ID supplied by the user.
      Arc_In   : Arc_Pointer;
      Arc_Out  : Arc_Pointer;
      Next     : Node_Pointer;
      Previous : Node_Pointer;
   end record;
type Arc is
   record
      ID     : Arc_ID;        -- Unique arc ID supplied by the user.
      Source : Node_Pointer;
      Target : Node_Pointer;
      Next   : Arc_Pointer;
   end record;
type Rank;
type Rank_Pointer is access Rank;
type Rank is
   record
      Number     : Level_ID;  -- Computed ordinal number of the rank.
      First_Node : Node_Pointer;
      Last_Node  : Node_Pointer;
      Next       : Rank_Pointer;
      Previous   : Rank_Pointer;
   end record;
    First_Rank : Rank_Pointer;
Last_Rank  : Rank_Pointer;
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
用注释来解释目的、结构、数据结构的语义对理解代码很有帮助。许多维护者在试图
理解单元实现时，都会先看数据结构。理解数据的储存，同时还有不同数据之间的
联系和数据在单元中的流程是理解单元内容的第一步。

在上面的第一个例子中，变量名 Current\_Column 和 Current\_Row 相对是自我注释的。
变量名 Desired\_Column 是个好名字。但他让读代码的人疑惑 Current\_Column 和
Desired\_Column 之间的关系。注释还解释了要有这两个变量的原因。

另一个注释数据声明的好处是一组数据声明注释可以代替可能分布在在代码中各个
数据被操作的地方。在上面的第一个例子中，注释简单扩展了``current'' 和``marked''
的意义。它陈述了当前(current)的方位是光标的位置。当前方位在当前缓冲中，
而标记 (marked) 方位是由用户标记的。这些注释，和辅助的变量名，大幅减少了
代码中需要注释的各个单独语句。

记录异常的完全意思及其被抛出的条件是很重要的，这在上面第二个例子中
可看到，特别是当异常是在规约包中声明的。如果没有读包体中的代码，读者没有
其他方法得到异常的准确意思。

象第二个例子中那样，把所有的异常都组在一起，就象给读者提供了一份特殊情况的
词汇表。当包中不同的子程序可以抛出同样的异常时，这个很有用。如果包中每个
异常只能被一个子程序抛出，那把异常和相关的子程序组在一起比较好。

当注释异常时，用一般用语来描述异常的意思比列出所有会抛出此异常的子程序更好;
那样的列表很难维护。当一个新例程加入后，这个列表很有可能没有跟着更新。
另外，这些信息在子程序的注释中已经有了，所有子程序能抛出的异常都应该在
注释中列出。列出子程序的异常比列出抛出异常的子程序要有用和更容易维护。

第三个例子中，记录的域名短而易记，但他们并不能完全自述。这通常
在包含访问类型的复杂的数据结构中发生。无论给记录和域起什么名字，都没有办法
完全解释整个记录和指针结构成为一个嵌套和排序的链表。例子中的注释就是这样，
很有用。没有了注释，读者不会知道哪些链表已经排序，哪些是双向链表和为什么。
注释解释了作者对这个复杂数据结构的意图。如果维护者想确认双向链表实现正确，
还是必需要阅读代码。有着这样的准备，当维护者阅读代码是，就比较容易找到
一个臭虫: 一个指针改变了，但另一个方向的没有。

有关注释重分派操作的原理，参见\ref{c:oof:tagged:dispatch}。(重分派是指
把一个基元操作的参数转化为一个类宽类型，然后分派到另一个基元操作。) 
在准则\ref{c:oof:tagged:dispatch}的原理中，讨论了这样的文档应该放在规约
文件还是体文件中。
\end{blockindent}

\subsection{语句注释}
\glentry{准则}
\begin{itemize}
    \item 尽量减少语句中包含的注释。
    \item 只注释代码不够清晰的部分。
    \item 注释代码中有意省略的地方。
    \item 不要用注释来改写代码。
    \item 不要解释别的地方的代码，例如被本单元调用的子程序。
    \item 当需要注释的时候，要让注释跟代码明显区分出来。
\end{itemize}

\glentry{范例}
\begin{blockindent}
下面是个注释的很糟糕的例子:
\begin{lstlisting}
...
-- Loop through all the strings in the array Strings, converting
-- them to integers by calling Convert_To_Integer on each one,
-- accumulating the sum of all the values in Sum, and counting them
-- in Count.  Then divide Sum by Count to get the average and store
-- it in Average. Also, record the maximum number in the global
-- variable Max_Number.

for I in Strings'Range loop
   -- Convert each string to an integer value by looping through
   -- the characters which are digits, until a nondigit is found,
   -- taking the ordinal value of each, subtracting the ordinal value
   -- of '0', and multiplying by 10 if another digit follows.  Store
   -- the result in Number.
   Number := Convert_To_Integer(Strings(I));
   -- Accumulate the sum of the numbers in Total.
   Sum := Sum + Number;
   -- Count the numbers.
   Count := Count + 1;
   -- Decide whether this number is more than the current maximum.
   if Number > Max_Number then
      -- Update the global variable Max_Number.
      Max_Number := Number;
   end if;
end loop;
-- Compute the average.
Average := Sum / Count;
\end{lstlisting}

下面的例子改正了上面的错误: 不在注释中重复代码中明显的内容;
不描述\texttt{Convert\_To\_Integer}内部的详细内容; 删除错误的代码
(在和的累加那行语句); 让剩下的少数注释更加明显从代码中区分出来。
\begin{lstlisting}
Sum_Integers_Converted_From_Strings:
   for I in Strings'Range loop
      Number := Convert_To_Integer(Strings(I));
      Sum := Sum + Number;
      Count := Count + 1;
      -- The global Max_Number is computed here for efficiency.
      if Number > Max_Number then
         Max_Number := Number;
      end if;
   end loop Sum_Integers_Converted_From_Strings;

Average := Sum / Count;
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
范例中注释的改善不单单是减少了注释的数量，而是减少了无用注释的数量。

改写或解释明显的代码的注释没有任何价值。他们浪费了作者的工夫去写，
也浪费了维护者的工夫去更新。所以，他们常常在后来变成不准确的。这样的注释
也让代码显得杂乱，隐藏了少数重要的注释。

描述别的单元内部的信息，违反了信息隐藏的原则。有关
\texttt{Convert\_To\_Integer} 的具体内容跟调用的单元无关，这些内容
最好一直隐藏着，以免算法改变了。试图解释别的地方的代码很难维护，
而且几乎总是在第一次代码修正时就变得不正确了。

让注释从代码中明显的显现出来，这样的好处是代码更容易阅读，少数重要的
注释也能突显出来。突显不寻常或特殊的代码的特性，以说明他们是有意的。
这帮助维护者在维护或移植时，把注意力集中到那些容易产生问题的代码部分。

注释应该记录不可移植的，实现依赖性的，环境依赖性的，或者棘手的代码。
他提醒读者这些不寻常的地方是有原因的。象解释针对编译器错误的变通方法
是个有用的注释。如果你用了比较低级别的解决方法 (在软件工程角度上看不是
``完美''的)，请注释它。注释包含的信息要陈述你选用某个构造的原因。
还要包含那些失败的尝试，例如，用高级别的构造。这些信息对维护者在
历史维护上有帮助。你对读者展现了在选择这个构造是进行了相当的思考。

最后，注释应该用来解释代码中所没呈现和存在的东西。如果你作了一个刻意的决定
不去进行某些动作，例如在使用结束后，释放某个数据结构，一定要记得
注释为什么不释放。否则，维护者可能注意到这个明显的乎略而``改正''
了，也就引入了错误。

在准则\ref{c:oof:tagged:primitive}中，讨论了对标签类型和重分派
该准备什么样的文档。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
对于上面的例子，还可以改进，把变量 \texttt{Count} 和 \texttt{Sum} 在
本地的块声明，这样他们的作用域就局限在本地，而且他们的初始化也靠近
使用的地方。例如，命名块为 \texttt{Compute\_Average} 或把代码转移到
一个名为 \texttt{Average\_Of} 的函数里。\texttt{Max\_Number} 的计算
也可以从 \texttt{Average} 中分离出来。但是这些改变有别的准则来参考，
这里的例子只是用来示范注释的使用。
\end{blockindent}

\subsection{标示注释}
\glentry{准则}
\begin{itemize}
\item 用分页标示来标识单元边界 (参见准则\ref{c:src:code_fmt:page})。
\item 如果包程序体、子程序体、任务体、或块的开始之前有声明部分，那在注释
里重复单元名字来标示他们的开始。
\item 对于很长或嵌套得很厉害的 \texttt{if} 或 \texttt{case} 语句，
在结束的地方用一个总结语句控制条件的注释来标示结束。
\item 对于很长或嵌套得很厉害的 \texttt{if} 语句，
用一个总结 \texttt{else} 部分语句控制条件的注释来标示\texttt{else} 部分。
\end{itemize}

\glentry{范例}
\begin{blockindent}
\begin{lstlisting}
if    A_Found then
   ...
elsif B_Found then
   ...
else  -- A and B were both not found
   ...
   if Count = Max then
      ...
   end if;
   ...
end if;  -- A_Found
------------------------------------------------------------------------
package body Abstract_Strings is
   ...
   ---------------------------------------------------------------------
   procedure Concatenate (...) is
   begin
      ...
   end Concatenate;
   ---------------------------------------------------------------------
   ...
begin  -- Abstract_Strings
   ...
end Abstract_Strings;
------------------------------------------------------------------------
\end{lstlisting}
\end{blockindent}

\glentry{原理}
\begin{blockindent}
标示注释突出了代码的结构，使得阅读起来更容易。他们可以是分隔代码段的线或
构造的说明标签。他们帮助读者了解在代码中的当前位置。这对大型的单元比小单元
更有用。一个小巧的标示可以和被注释的内容在一行内，因此他增加了信息而没有引起
混乱。

\texttt{if, elsif, else} 和 \texttt{end if} 经常被长序列的语句分离，有时
还包含其他的 \texttt{if} 语句。如上面第一个范例中，标示注释突出了同个语句
中和相距比较远的对应关键词间的联系。对于块语句和循环语句，标签注释不是必须的，
因为这些语句的语法允许命名并且在结束时重复。用名字比用标示注释更好，
因为编译器在开始还和结束时会检验名字。

包体内的语句序列通常离包的第一行比较远。许多子程序体会先出现，
每个都可能包含许多行。在第二个范例中，标签注释突出了和包开始时的对应关系。
\end{blockindent}

\glentry{注解}
\begin{blockindent}
如果过分重复名字和加注条件表达式会让代码很混乱。
标示注释的好处在于他的视觉距离，特别是分页。
\end{blockindent}

\input{060.010-readability-type}

\section{总结}
\glentry{拼写}
\noindent
\begin{itemize}
    \item 用下划线分隔复合名字中的词。
    \item 使用一致的风格来呈现数字。
    \item 使用对问题适当的根植来呈现字面值。
    \item 使用下划线来分隔数字，就像在普通文字中用逗号或句号
(或对于非十进制用空格)。
    \item 当使用科学标示法，用一致的大写或小写的E。
    \item 在非十进制数字中，要么使用全大写或小写来标示其中的字母。
    \item 让保留字和程序的其他组件从视觉上分开。
    \item 如果一个短小的同义词存在，不要使用长词语的缩写。
    \item 用一致的缩写策略。
    \item 不要用有多种解释的缩写。
    \item 缩写必须比原词节省了许多字符，以认可它的使用。
    \item 使用应用领域通用的缩写。
    \item 维护一个缩写列表，只使用列表中的缩写。
\end{itemize}

\glentry{命名约定}
\noindent
\begin{itemize}
    \item 尽量选择可以自我说明的名字。
    \item 使用短的同义词而不用缩写。
    \item 使用应用的名词，但是不要用偏僻的术语。
    \item 避免使用同一个名字来声明不同类型的标识符。
    \item 使用单数、泛指的名词作为子类型标志符。
    \item 选用能说明子类型值的标志符。
    \item 对定义可见的访问类型、子界、数组，考虑在子类型标志符中用后缀。
    \item 对私有类型，不要使用针对子类型标志符的构造规则 (例如加后缀)。
    \item 不要使用预定义包中的子类型名字。
    \item 对于逻辑对象 使用判断语或从属的名字。
    \item 使用单数、泛指的名词作为对象标志符。
    \item 选用能说明对象执行时的值的标识符。
    \item 使用单数、泛指的名词作记录的组件。
    \item 使用一致的命名规则来对标签类型和相对应包命名。

    \item 用行动的动词来命名过程和入口。
    \item 用判断式的子句命名逻辑函数。
    \item 用名词命名非逻辑函数。
    \item 给包的名字要暗含比子程序高级的组构。通常来说，这都是描述供应的抽象的
名词。
    \item 给任务命名隐含活动的实体。
    \item 用描述被保护数据的名词来命名保护单元。
    \item 把通用子程序当做非通用子程序来命名。
    \item 把通用包当做非通用包来命名。
    \item 通用名字应该比例示的名字更广泛。

    \item 尽量用符号的名字而不是文字。
    \item 对于数学常量 \texttt{Pi} 和 \texttt{e},
用预定义的常量 \texttt{Ada.Numerics.Pi} 和 \texttt{Ada.Numerics.e}。
    \item 用常量而不是变量来表示常数。
    \item 当一个值是某个类型所特有的或这个值必须静止，用常量。
    \item 尽量用命名的数字，而不是常量。
    \item 用命名的数字来代替文字的数字，它的类型或属性是真正的通用的。
    \item 对于对象的值在确立之后不能改变的，用常量(\cite{united87})。
    \item 用静止表达式来表示符号间的关系。
    \item 用线性的独立的文字组。
    \item 尽量用属性 \texttt{'First} 或者 \texttt{'Last} 而不是文字。
    \item 用异常所代表的问题种类命名。
    \item 使用前缀 \texttt{New}, \texttt{Make} 或 \texttt{Create}
命名构造器 (这里指创造或初始化对象的操作)。
    \item 对于含有构造器的子包，用可以标示内容的名字。
\end{itemize}

\glentry{注释}
\noindent
\begin{itemize}
    \item 尽可能写清晰的代码来减少注释的需求。
    \item 绝不要在注释里重复可以从代码中轻易获得的信息。
    \item 在需要注释的地方，要写得简明和完整。
    \item 注释中要用正确的语法和拼写。
    \item 注释要在视觉上和代码区分出来。
    \item 把注释放在数据头上，这样相关的信息可以被自动工具提取出来。

    \item 在每一个源文件中放一个文件头。
    \item 在文件头中，放入所有者、责任和历史信息。

    \item 在每一个程序单元规约都放一个头。
    \item 在程序单元规约头中放上用户使用要求信息。
    \item 除了单元名字，不要在头中重复规约中的信息。
    \item 解释单元做些什么，而不是怎样和为什么这么做。
    \item 描述这个程序单元的完整接口，包括它能抛出的任何异常和任何可以造成
全局影响的地方。
    \item 不要包括这个单元如何适用于包含的软件系统的信息。
    \item 描述单元的性能特性 (时间、空间)。

    \item 单元维护者需要的信息。
    \item 解释单元如何以及为什么进行它的功能，而不是单元做什么。
    \item 不要重复阅读代码就能轻易得到的信息(除了单元名字)。
    \item 不要重复规约头中有的信息(除了单元名字)。

    \item 除非他们的名字能自我解释，否则注释所有的数据类型，对象和异常。
    \item 从语意上注释复杂的指针类的数据结构。
    \item 注释数据对象之间存在的关系。
    \item 乎略名字中包含的信息。
    \item 对于标签类型，如果你期望它的特殊化(例如衍生类型)复写某些重分派
    操作，注释重分派的信息。

    \item 用分页标示来标识单元边界 (参见准则\ref{c:src:code_fmt:page})。
    \item 如果包程序体、子程序体、任务体、或块的开始之前有声明部分，那在注释
    里重复单元名字来标示他们的开始。
    \item 对于很长或嵌套得很厉害的 \texttt{if} 或 \texttt{case} 语句，
    在结束的地方用一个总结语句控制条件的注释来标示结束。
    \item 对于很长或嵌套得很厉害的 \texttt{if} 语句，
    用一个总结 \texttt{else} 部分语句控制条件的注释来标示\texttt{else} 部分。
\end{itemize}

\glentry{类型的使用}
\noindent
\begin{itemize}
    \item 尽量限制标量类型的值域。
    \item 从应用中查找可能值的信息。
    \item 不要重用标准 (Standard) 包中的子类型名称。
    \item 用子类型声明来促进程序的可读性 (\cite{booch87})。
    \item 协调使用导出类型和子类型 (\ref{c:prog-prac:types:derived})。

    \item 用枚举类型替代数字码。
    \item 只有在绝对必要的时候，才用表示语句来匹配外部设备的需求。
\end{itemize}
